﻿using System;
using System.Net;
using SilverlightDatabase.SilverlightDatabaseServer;
using System.Collections.Generic;
using System.IO;
using SilverlightDatabase.Compression;

namespace SilverlightDatabase.Client
{
    /// <summary>
    /// Client is used to communicate with server component
    /// </summary>
    public class Client
    {
        /// <summary>
        /// Create new instance of client
        /// </summary>
        public Client() { }

        EventHandler<ClientResult> _completedHanlder = null;
        SilverlightDatabaseServerClient _client = null;

        /// <summary>
        /// Send database to the server
        /// </summary>
        /// <param name="database">Database to send</param>
        /// <param name="completed">Handler that will be invoked when send process compltes</param>
        public void PutDatabase(Database database, EventHandler<ClientResult> completed)
        {
            PutDatabase(database, completed, null);
        }

        /// <summary>
        /// Send database to the server
        /// </summary>
        /// <param name="database">Database to send</param>
        /// <param name="completed">Handler that will be invoked when send process compltes</param>
        /// <param name="userState">User state</param>
        public void PutDatabase(Database database, EventHandler<ClientResult> completed, object userState)
        {
            _completedHanlder = completed;
            _client = new SilverlightDatabaseServerClient();
            SilverlightDatabaseData data = GetSilverlightDatabaseData(database);


            _client.PutDatabaseCompleted += new EventHandler<PutDatabaseCompletedEventArgs>(client_PutDatabaseCompleted);
            _client.PutDatabaseAsync(data, userState);
        }

        void client_PutDatabaseCompleted(object sender, PutDatabaseCompletedEventArgs e)
        {
            _client.PutDatabaseCompleted -= client_PutDatabaseCompleted;
            _client.CloseAsync();
            _completedHanlder.Invoke(this, new ClientResult(null, e.Result, e.Error, e.UserState));
        }

        private SilverlightDatabaseData GetSilverlightDatabaseData(Database database)
        {
            SilverlightDatabaseData data = new SilverlightDatabaseData();
            Dictionary<string, byte[]> clientData = new Dictionary<string, byte[]>();
            using (MemoryStream stream = new MemoryStream())
            {
                database.WriteDatabaseToStream(stream);
                clientData.Add(database.DatabaseName, stream.ToArray());
            }
            foreach (var table in database.Tables)
            {
                using (MemoryStream stream = new MemoryStream())
                {
                    table.WriteDTableToStream(stream);
                    clientData.Add(table.RowType.FullName, stream.ToArray());
                }
            }
            data.DatabaseName = database.DatabaseName;
            data.Password = database.Password;
            data.UseCompression = database.UseCompression;
            data.ClientDatabaseData = clientData;
            return data;
        }

        private Database ConvertToDatabase(SilverlightDatabaseData data)
        {
            Database database = null;
            if (data.ClientDatabaseData != null)
            {
                string[] parts;
                Type tableType = null;
                byte[] databaseDefinition = data.ClientDatabaseData[data.DatabaseName];
                using (MemoryStream stream = new MemoryStream(databaseDefinition))
                {
                    StreamReader reader = new StreamReader(stream);
                    string content;
                    if (data.UseCompression)
                    {
                        content = CompressionUtility.DecompressToString(CompressionUtility.ReadFullStream(stream));
                    }
                    else
                    {
                        content = reader.ReadToEnd();
                    }
                    if (!string.IsNullOrEmpty(data.Password))
                    {
                        content = Cryptography.Decrypt(content, data.Password);
                    }
                    parts = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                    reader.Close();
                    stream.Close();
                    database = Database.CreateDatabase(data.DatabaseName, data.Password, data.UseCompression, true);
                }

                if (parts != null)
                {
                    for (int counter = 1; counter < parts.Length; counter++)
                    {
                        string content = string.Empty; ;
                        string rowTypeString = parts[counter];
                        Type rowType = Type.GetType(rowTypeString);
                        tableType = typeof(Table<>).MakeGenericType(new Type[] { rowType });

                        byte[] tableDefinition = data.ClientDatabaseData[rowTypeString.Substring(0, rowTypeString.IndexOf(",")).Trim()];
                        using (MemoryStream stream = new MemoryStream(tableDefinition))
                        {
                            StreamReader reader = new StreamReader(stream);
                            if (data.UseCompression)
                            {
                                content = CompressionUtility.DecompressToString(CompressionUtility.ReadFullStream(stream));
                            }
                            else
                            {
                                content = reader.ReadToEnd();
                            }
                            if (!string.IsNullOrEmpty(data.Password))
                            {
                                content = Cryptography.Decrypt(content, data.Password);
                            }

                            reader.Close();
                            stream.Close();
                        }


                        database.LoadTable(content, tableType);

                    }
                }
            }
            return database;
        }

        /// <summary>
        /// Get database from server
        /// </summary>
        /// <param name="databaseName">Database name to get</param>
        /// <param name="password">Password to use</param>
        /// <param name="useCompression">Use compression </param>
        /// <param name="completed">Handler to be invoked when process compeltes</param>
        public void GetDatabase(string databaseName, string password, bool useCompression, EventHandler<ClientResult> completed)
        {
            GetDatabase(databaseName, password, useCompression, completed, null);
        }

        /// <summary>
        /// Get database from server
        /// </summary>
        /// <param name="databaseName">Database name to get</param>
        /// <param name="password">Password to use</param>
        /// <param name="useCompression">Use compression </param>
        /// <param name="completed">Handler to be invoked when process compeltes</param>
        /// <param name="userState">User state</param>
        public void GetDatabase(string databaseName, string password, bool useCompression, EventHandler<ClientResult> completed, object userState)
        {
            _completedHanlder = completed;
            _client = new SilverlightDatabaseServerClient();
            _client.GetDatabaseCompleted += new EventHandler<GetDatabaseCompletedEventArgs>(_client_GetDatabaseCompleted);
            _client.GetDatabaseAsync(databaseName, password, useCompression, userState);
        }

        void _client_GetDatabaseCompleted(object sender, GetDatabaseCompletedEventArgs e)
        {
            _client.GetDatabaseCompleted -= _client_GetDatabaseCompleted;
            _client.CloseAsync();
            _completedHanlder.Invoke(this, new ClientResult(ConvertToDatabase(e.Result), (e.Result != null), e.Error, e.UserState));
        }

        /// <summary>
        /// Send database to the server and get updated database
        /// </summary>
        /// <param name="database">Database to send</param>
        /// <param name="completed">Handler to be invoked when process completes</param>
        /// <param name="userState">User state</param>
        public void UpdateDatabase(Database database, EventHandler<ClientResult> completed, object userState)
        {
            _completedHanlder = completed;
            _client = new SilverlightDatabaseServerClient();
            _client.UpdateDatabaseCompleted += new EventHandler<UpdateDatabaseCompletedEventArgs>(_client_UpdateDatabaseCompleted);
            SilverlightDatabaseData data = GetSilverlightDatabaseData(database);
            _client.UpdateDatabaseAsync(data, userState);
        }

        void _client_UpdateDatabaseCompleted(object sender, UpdateDatabaseCompletedEventArgs e)
        {
            _client.UpdateDatabaseCompleted -= _client_UpdateDatabaseCompleted;
            _client.CloseAsync();
            _completedHanlder.Invoke(this, new ClientResult(ConvertToDatabase(e.Result), (e.Result != null), e.Error, e.UserState));
        }

        /// <summary>
        /// Send database to the server and get updated database
        /// </summary>
        /// <param name="database">Database to send</param>
        /// <param name="completed">Handler to be invoked when process completes</param>
        public void UpdateDatabase(Database database, EventHandler<ClientResult> completed)
        {
            UpdateDatabase(database, completed, null);
        }
    }
}
